/*

	tools23d - tools for converting images to 3D formats.
    Copyright (C) 2014  Dave Gorst

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

*/

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <time.h>

#include "tools23d.h"
#include "util.h"
#include "glob-all.h"

static unsigned int current_globule = 0;
static globule ** globules_list;

/**
Initialise a new globule

@return Index of new globule, or ::UNABLE_TO_INITIALISE_GLOBULE on failure to allocate memory.
*/

unsigned int T3D_InitialiseGlob ()
{
	globule * new_globule;
	globule ** realloc_ptr;

    if (current_globule == UNABLE_TO_INITIALISE_GLOBULE - 1)
        return UNABLE_TO_INITIALISE_GLOBULE;

	current_globule++;

	if (current_globule == 1)
		globules_list = (globule **) malloc (sizeof (globule *));
	else
	{
		realloc_ptr = (globule **) realloc (globules_list, current_globule * sizeof (globule *));
		if (realloc_ptr == NULL)
		{
			free (realloc_ptr = globules_list);
			return UNABLE_TO_INITIALISE_GLOBULE;
		}
		else
			globules_list = realloc_ptr;
	}


	if (globules_list != NULL)
	{
		if ((new_globule = (globule *) malloc (sizeof (globule))) != NULL)
		{

			new_globule->start = (file_entry *) NULL;
			new_globule->end = (file_entry *) NULL;
			new_globule->number_of_files = 0;

			globules_list [current_globule - 1] = new_globule;

			return (current_globule - 1);
		}
		else
			return UNABLE_TO_INITIALISE_GLOBULE;
	}
	else
		return UNABLE_TO_INITIALISE_GLOBULE;
}

/**
Destroy a globule

@param  globule Index of globule to destroy and free.
*/

void T3D_DestroyGlob (unsigned int globule)
{
	unsigned int index;

 	if (globules_list [globule] != NULL)
	{
		for (index = globules_list [globule]->number_of_files; index; --index)
		{
			free ((globules_list [globule]->files_list [index-1])->filepath);
			free (globules_list [globule]->files_list [index-1]);
		}

		if (globules_list [globule]->number_of_files)
			free (globules_list [globule]->files_list);
		free (globules_list [globule]);
	}

	globules_list [globule] = NULL;

}

/**
Destroy and free all globules then free the list containing them.

*/

void T3D_ShutdownGlob (void)
{
	while (current_globule)
		T3D_DestroyGlob (--current_globule);

	free (globules_list);
}

/**
Sort contents of globule into order

@param  globule Index of globule to sort.
@param  sorttype    Enum indicating whether to sort alphanumerically or by file change/creation time.
@return ::TRUE if successful. ::FALSE if the sort type was unknown.
*/

BOOL T3D_SortGlobule (unsigned int globule, SortType sorttype)
{
    switch (sorttype)
    {
        case Alphanumeric:
            qsort (globules_list[globule]->files_list, globules_list[globule]->number_of_files, sizeof(char *), &T3D_CompareStrings);
            return TRUE;
        case Filechangetime:
            qsort (globules_list[globule]->files_list, globules_list[globule]->number_of_files, sizeof(char *), &T3D_CompareTimestamps);
            return TRUE;
        default:
            T3D_SetLastError("Unknown sort type specifed.");
            return FALSE;
    }
}

/**
Append new entry to globule.

@param  globule Index of globule.
@param  new_filename    Name of file to add.
@param  file_creation_time  Change/creation datetime of added file.
@return Index of added file.
*/

unsigned int T3D_AppendEntry (unsigned int globule, char * new_filename, time_t file_creation_time)
{
	file_entry * new_entry;
	file_entry ** realloc_ptr;

    unsigned int temp;
	unsigned int filename_length = strlen (new_filename);

	if (globule < current_globule)
	{
		if (!globules_list [globule]->number_of_files)
			globules_list [globule]->files_list = (file_entry **) malloc (sizeof (file_entry *));
		else
		{
			realloc_ptr = (file_entry **) realloc (globules_list [globule]->files_list, (globules_list [globule]->number_of_files + 1) * sizeof (file_entry *));

			if (realloc_ptr == NULL)
			{
				free (realloc_ptr = globules_list [globule]->files_list);
				return UNABLE_TO_INITIALISE_GLOBULE;
			}
			else
				globules_list [globule]->files_list = realloc_ptr;
		}

		if (globules_list [globule]->files_list != NULL)
		{
			if ((new_entry = (file_entry *) malloc (sizeof (file_entry))) != NULL)
			{
				if ((new_entry->filepath = (char *) malloc ((filename_length + 1) * sizeof (char))) != NULL)
				{
					for (temp = 0; temp < filename_length; temp++)
						new_entry->filepath [temp] = new_filename [temp];

					new_entry->filepath [filename_length] = '\0';

					new_entry->change_time = file_creation_time;

					if (!globules_list [globule]->number_of_files)
					{
						globules_list [globule]->start = globules_list [globule]->end = new_entry;

						new_entry->previous = new_entry->next = (file_entry *) NULL;
					}
					else
					{
						globules_list [globule]->end->next = new_entry;

						globules_list [globule]->end = new_entry;
					}

					globules_list [globule]->files_list [globules_list [globule]->number_of_files] = new_entry;

					return (++globules_list [globule]->number_of_files);
				}
				else
				{
					free (new_entry);
					return UNABLE_TO_INITIALISE_GLOBULE;
				}
			}
			else
				return UNABLE_TO_INITIALISE_GLOBULE;
		}
		else
			return UNABLE_TO_INITIALISE_GLOBULE;
	}
	else
		return 0;
}

/**
Append new entry to globule.

@param  globule Index of globule.
@param  index   Entry within globule.
@return Pointer to globule entry.
*/

file_entry * T3D_RetrieveGlobEntry (unsigned int globule, unsigned int index)
{
	if ((globule < current_globule) && (index < globules_list [globule]->number_of_files))
		return globules_list [globule]->files_list [index];
	else
		return NULL;
}

/**
Return number of entries present in globule.

@param  globule Index of globule to check.
@return Number of entries.
*/


unsigned int T3D_GetNumberOfEntries (unsigned int globule)
{
	return (globule < current_globule ? globules_list [globule]->number_of_files : 0);
}
